#!/usr/bin/python
import socket, sys, threading, Queue, errno, string
import userHandler, msgHandler
from time import gmtime, strftime
from datetime import date
import xml.sax
import MySQLdb
import Base
import hashlib
import user

HOST = ''
PORT = 18000

users = {}


def sendToAll(msg) :
    #this kind of message will be sent
    #<rMessage From="sender" To="recipient" char_nick="char_nick" content="content" />
    sent = 0
    print "dans sent to all"
    print "il y a", len(users), "utilisateur(s) enligne"
    for u in users :
        print 'u :', u
        #conn = users[u]['socket']
        conn = users[u].socket
        print "socket conn", conn
        #msg = '[' + mail + '] : ' + msg
        msg += '\0'
        print msg
        try :
            sent = conn.send(msg)
        except socket.error, e :
            print "A socket error"
        except IOError, e:
            if e.errno == errno.EPIPE : 
                print "EPIPE error"
            else :
                print "Other error"
        print sent, "characters sent"


def sendToUser(recip, msg):
    #this kind of message will be sent
    #<rMessage From="sender" To="recipient" char_nick="char_nick" content="content" />
    sent = 0
    print "dans sent to user"
    print "il y a", len(users), "utilisateur(s) enligne"
    conn = users[recip].socket
    msg += '\0'
    try :
        sent = conn.send(msg)
    except socket.error, e :
        print "A socket error"
    except IOError, e:
        if e.errno == errno.EPIPE : 
            print "EPIPE error"
        else :
            print "Other error"
    print sent, "characters sent"
    


def mySend(sendBuff, conn) :
    try :
        sent = conn.send(sendBuff)
        print sendBuff
    except socket.error :
        if sent < 0 :
            print "Error send ()"
    print sent, "characters sent"



def parser(buff):
    print buff
    handler = userHandler.UserHandler()
    fichier = open("machin.xml", 'w')
    print "len(buff) :", len(buff)
    k=0
    for i in buff :
        if k < len(buff) - 1 :
            fichier.write(i)
        k=k+1
    fichier.close()
    parseur = xml.sax.make_parser()
    parseur.setContentHandler(handler)
    parseur.parse("machin.xml")
        
def sendPolicy(conn):
    ###############################
    #path = "../../flashpolicy.xml"
    path = "./crossdomain.xml"
    ###############################
    f = open(path, 'r')
    policy = f.read(10001)
    if (len(policy) > 10000) :
        raise exceptions.RuntimeError("File probably too large to be a policy file", path)
    if "cross-domain-policy" not in policy :
        raise exceptions.RuntimeError("Invalid policy file", path)
    f.close()
    policy += '\0'
    sent = conn.send(policy)
    print policy
    print sent, "characters sent"


def waitForUser(connexion) :
    connexion.setblocking(1)
    client = user.User(connexion)
    idClient = -1
    #connexion.settimeout(10)
    print "wait for user's messages"
    print connexion
    #================ HANDLER ==================
    handler = userHandler.UserHandler()
    #===========================================
    tag = ""
    buff = "_"
    while buff != "" : 
        #print date and time
        print strftime("%a, %d %b %Y %H:%M:%S GMT +0000", gmtime()) #########################################################
        
        tag = ""
        buff = ""
        
        #receptionne le login du client
        print "wait for message WFU..." #########################################################
        
        buff = connexion.recv(1024)
                
        buff = buff.rstrip('\0')
        print buff #########################################################
        print "len(buff) :", len(buff) #########################################################
    
        """
        cela merite un chapitre pour expliquer le probleme
        Qd on recoit un string ici une balise xml de la forme
        <machin chose /> et on le met dans un string comme ceci
        buff = connexion.recv(1024)
        il met bien les caracteres MAIS il ajoute un blanc de plus
        ==> si notre string fais 40 caracteres, len(buff) va donner 41
        ===> c'est ca ki fait foire le parsage ... le dernier blanc
        
        la boucle for ci-dessous ecrit caractere par caractere du string buff
        dans un fichier sauf le dernier blanc   
        """
   
        try :
            xml.sax.parseString(buff, handler)
        except :
            if len(buff) == 0 :
                p = 7
                print "Parsing error, empty buffer" #########################################################
    
        tag = handler.tag
        print "tag :", tag #########################################################

        if tag == "getMap" :
            l = LandSquares(handler.topLeft, handler.bottomRignt)
            connexion.send(l.formatXML())
        
        
        if tag == "login" :
            print "==== dans handler ===="
            print handler.mail
            print handler.passw
        
            mail = handler.mail
            passe = handler.passw
            print 'mail :', mail
            r = "select * from users where email=\""+mail+"\" and pwd=\""+passe+"\""
            print r
            cur = b.req(r)
        
            if(cur.rowcount == 1) :
                res = cur.fetchall()
                for row in res :
                    if row["email"] == mail :
                        id = row["id"]
                        print "id dans login", id
                        client.id = id
                        client.mail = mail
                        idClient = id;
                print "ok"

                r = "UPDATE users SET lastLogin=now() WHERE email=\""+mail+"\""
                print r
                b.req(r)

                #get the name of the character
                r = "SELECT charac_nick, cx, cy FROM characters where user_id="+str(id)
                print r
                res = b.req(r).fetchone()
                client.char_nick = res['charac_nick']

                #get character's position
                r = "SELECT "

                
                buffR = "<rLogin conn=\"ok\" id=\""+str(id)+"\" email=\""+mail+"\"/>\0"
                mySend(buffR, connexion)
                buff = "<userOnline id=\""+str(id)+"\" char_nick=\""+client.char_nick+"\" x=\""+str(client.x)+"\" y=\""+str(client.y)+"\" />"
                print buff
                sendToAll(buff)
                
                #met le client dans le dictionnaire
                #u={}
                #u['mail']=mail
                #u['socket']=connexion
                #users[id]=u
                print client
                users[id] = client
                print users
                #users.update({id : connexion})

                """
                t2 = (id,)
                #creer un thread pour attendre des messages du client
                t = threading.Thread(target = waitForMsg, args = t2)
                t.start()
                print "thread WFM lance"
                #t.join()
                """
                #when sending a message to a xml socket, the message has to terminate by a \0
                #This took me ONE WEEK to find out !!!!
            
            else :
                print "There is a problem with your e-mail address and/or password"
                buffR = "<rLogin conn=\"failed\" />\0"
                mySend(buffR, connexion)


        if tag == "message" :
            print "#####################"
            sender = handler.From
            recipient = handler.To
            content = handler.content
            r = "SELECT charac_nick FROM characters where user_id="+str(id)
            print r
            res = b.req(r).fetchone()
            client.char_nick = res['charac_nick']
            char_nick = client.char_nick
            print "from:", sender
            print "to:", recipient
            print "message:", content
            print "char_nick:", char_nick
            print client
            buffR = "<rMessage From=\""+sender+"\" To=\""+recipient+"\" char_nick=\""+char_nick+"\" content=\""+content+"\" />"
            if recipient != "0" :
                sendToUser(recipient, content)
            else :
                sendToAll(buffR)


        if tag == "move" :
            charac = handler.charac_id
            cx = handler.x
            cy = handler.y
            r = "select crossable from landType where lt_name=(select type from land where x="+cx+" and  y="+cy+")"
            print r
            res = b.req(r).fetchone()
            cross = res["crossable"]
            #if the landType is crossable, tell every one that the character is moved
            if cross != 0 :
                sendToAll(charac, buff, 0)


        if tag == "onlineUsers" :
            rep = "<rOnlineUsers>\n"
            for i in users :
                print i, "\n", users[i]
                #print user[i].mail
                client = users[i]
                print client.mail
                rep += "\t<user id=\""+str(i)+"\" char_nick=\""+users[i].char_nick+"\" x=\""+str(users[i].x)+"\" y=\""+str(users[i].y)+"\" />\n"
            rep += "</rOnlineUsers>\0"
            mySend(rep, connexion)
            """
            print rep
            sent = connexion.send(rep)
            if sent == 0 :
                raise RuntimeError
            else :
                print sent, "characters sent"
            """
            
        if tag == "policy-file-request" :
            sendPolicy(connexion)
            print "policy file sent"
            #print "policy file requested"



        if tag == "quit" :
            connexion.close()
            #remove the user from the dictionnary
            del users[handler.id]
            print "connection closed"

    ##############################################################
        
        if tag == "register" : 
            print "==== dans handler ===="
            print handler.mail
            print handler.passw
        
            mail = handler.mail
            passe = handler.passw
            # e-mail verification : existing e-mail can not register
            r = "select * from users where email=\""+mail+"\""
            print r
            if(b.req(r).rowcount == 1) :
                print "ok"
                buffR = "<rRegister msg=\"emailUsed\" />\0"
                mySend(buffR, connexion)
                
            else :
                r = "insert into users (email, pwd, firstJoin, firstName, lastName, sex, country) values(\""+mail+"\", \""+passe+"\", now(), '-', '-', 'M', '-')"
                print r
                b.req(r)
                buffR = "<rRegister msg=\"user inserted\" />\0"
                mySend(buffR, connexion)

                
                r = "select * from users where email='"+mail+"'"
                print r
                cur = b.req(r)
                row = cur.fetchone()
                id = row["id"]
                #create a character for new user
                r = "insert into characters (charac_nick, user_id, character_type, health, mood, hunger, money, xp, cx, cy) values('"+mail+"', "+str(id)+", 'collector', 100, 100, 50, 10, 0, 0, 0)"
                print r
                b.req(r)
            
            

        if tag == "userDetailsReq" :
            mail = handler.mail
            r = "select id, firstName, lastName, sex, countryIndex, birthDay from users where email='"+mail+"'"
            i = b.req(r).fetchone()
            firstName = i["firstName"]
            print "firstName :", firstName
            lastName = i["lastName"]
            print "lastName :", lastName
            sex = i["sex"]
            print "sex :", sex
            countryIndex = i["countryIndex"]
            ID = i["id"]
            print "ID", ID, type(ID)
            r = "select charac_nick from characters where user_id="+str(ID)
            j = b.req(r).fetchone()
            nick = j["charac_nick"]
            print "countryIndex :", countryIndex
            print "nick :", nick
            birth = i["birthDay"]
            print birth;
            if birth != None :
                print "day", birth.day
                print "month", birth.month
                print "year", birth.year
            else :
                birth = date.today()
                print birth
            ########################################################
            print "before"
            for i in users :
                print i, users[i]
            
            client.id = ID;
            client.firstName = firstName
            client.lastName = lastName
            client.mail = mail
            client.char_nick = nick
            client.countryIndex = countryIndex
            users[ID] = client

            print "after"
            for i in users :
                print i, users[i]
            ########################################################
            
            buffR = "<rUserDetailsReq id=\""+str(ID)+"\" firstName=\""+firstName+"\" lastName=\""+lastName+"\" sex=\""+sex+"\" countryIndex=\""+str(countryIndex)+"\" nick=\""+nick+"\" bDay=\""+str(birth.day)+"\" bMonth=\""+str(birth.month)+"\" bYear=\""+str(birth.year)+"\" />\0"
            mySend(buffR, connexion)

            """
            sent  = connexion.send(buffR)
            print buffR
            if sent == 0 :
                raise RuntimeError
            else :
                print sent, "characters sent"
            """

            
        if tag == "updateProfile" :
            ID = handler.id
            mail = handler.mail
            firstName = handler.firstName
            lastName = handler.lastName
            pwd = handler.pwd
            sex = handler.gender
            country = handler.country
            countryIndex = handler.countryIndex
            nick = handler.nick
            birth = handler.birth
            # e-mail verification : can not change to an existing e-mail
            r = "select * from users where email=\""+mail+"\""
            print r
            cur = b.req(r)
            if(cur.rowcount == 1) :
                row = cur.fetchone()
                print "ID", ID, type(ID)
                print "row['id']", row['id'], type(ID)
                if int(row['id']) != int(ID) :
                    buffR = "<rUpdateProfile msg=\"emailUsed\" />\0"
                    mySend(buffR, connexion)
                
                else :
                    print "pwd", pwd
                    if pwd == "da39a3ee5e6b4b0d3255bfef95601890afd80709" :
                        print "PASS VIDE"
                        r = "update users set firstName='"+firstName+"', lastName='"+lastName+"', sex='"+sex+"', country='"+country+"', countryIndex='"+countryIndex+"', email='"+mail+"', birthday='"+birth+"' where id="+ID
                        print r;
                        i = b.req(r).fetchone()
                        print "i", i
                    else :
                        r = "update users set firstName='"+firstName+"', lastName='"+lastName+"', pwd='"+pwd+"', sex='"+sex+"', country='"+country+"', countryIndex='"+countryIndex+"', email='"+mail+"', birthday='"+birth+"' where id="+ID
                        print r;
                        i = b.req(r).fetchone()
                        print "i", i
                    ########################################################
                    client.firstName = firstName
                    client.lastName = lastName
                    client.mail = mail
                    client.char_nick = nick
                    client.countryIndex = countryIndex
                    print "ID", ID, type(ID)
                    users[long(ID)] = client
                    ########################################################
            
                    r = "update characters set charac_nick='"+nick+"' where user_id="+ID
                    print r;
                    i = b.req(r).fetchone()
                    print "i", i

    if idClient != -1 :
        del users[idClient]
    print "fin while"
    connexion.close()
    print "connection closed (while)"
            
        
###########################################################################

def waitForUserBis(connexion) :
    #connexion.setblocking(1)
    #connexion.settimeout(10)
    #print date and time
    print strftime("%a, %d %b %Y %H:%M:%S GMT +0000", gmtime())
    
    tag = ""
    buff = ""
    #mailValide = "unknown"
    #================ HANDLER ==================
    handler = userHandler.UserHandler()
    #===========================================
    
    
    #receptionne le login du client
    print "wait for message WFU..."
    
    buff = connexion.recv(1024)
    
    buff = buff.rstrip('\0')
    print buff
    print "len(buff) :", len(buff)
    
    """
    cela merite un chapitre pour expliquer le probleme
    Qd on recoit un string ici une balise xml de la forme
    <machin chose /> et on le met dans un string comme ceci
    buff = connexion.recv(1024)
    il met bien les caracteres MAIS il ajoute un blanc de plus
    ==> si notre string fais 40 caracteres, len(buff) va donner 41
    ===> c'est ca ki fait foire le parsage ... le dernier blanc
    
    la boucle for ci-dessous ecrit caractere par caractere du string buff
    dans un fichier sauf le dernier blanc   
    """
   
    try :
        xml.sax.parseString(buff, handler)
    except :
        if len(buff) == 0 :
            print "Parsing error, empty buffer"
            
    tag = handler.tag
    print "tag :", tag
        
    """
    if tag == "policy-file-request" :
    #sendPolicy(connexion)
    #print "policy file sent"
    print "policy file requested"
    """ 
        
    if tag == "login" :
        print "==== dans handler ===="
        print handler.mail
        print handler.passw
        
        mail = handler.mail
        passe = handler.passw
        print 'mail :', mail
        r = "select * from users where email=\""+mail+"\" and pwd=\""+passe+"\""
        print r
        cur = b.req(r)
        
        if(cur.rowcount == 1) :
            res = cur.fetchall()
            for row in res :
                if row["email"] == mail :
                    id = row["id"]
            print "ok"

            """
            sent = connexion.send("yahoooo\0")
            if sent == 0 :
            raise RuntimeError
            print sent, "characters sent"
            """
            
            r = "UPDATE users SET lastLogin=now() WHERE email=\""+mail+"\""
            print r
            b.req(r);
            try :
                sent = connexion.send("<rLogin conn=\"ok\" id=\""+str(id)+"\" email=\""+mail+"\"/>\0")
            except socket.error :
                if sent < 0 :
                    print "Error send ()"
            print sent, "characters sent"
            #met le client dans le dictionnaire
            #u={}
            #u['mail']=mail
            #u['socket']=connexion
            #users[id]=u
            #print users
            #users.update({id : connexion})

            """
            t2 = (id,)
            #creer un thread pour attendre des messages du client
            t = threading.Thread(target = waitForMsg, args = t2)
            t.start()
            print "thread WFM lance"
            #t.join()
            """
            #when sending a message to a xml socket, the message has to terminate by a \0
            #This took me ONE WEEK to find out !!!!
            
            
            
        else :
            print "There is a problem with your e-mail address and/or password"
            try :
                sent = connexion.send("<rLogin conn=\"failed\" />\0")
            except socket.error :
                if sent < 0 :
                    print "Error send ()"
                print sent, "characters sent"
    ##############################################################
        
    if tag == "register" : 
        print "==== dans handler ===="
        print handler.mail
        print handler.passw
        
        mail = handler.mail
        passe = handler.passw
        # e-mail verification : existing e-mail cannot register
        r = "select * from user where email=\""+mail+"\""
        print r
        if(b.req(r).rowcount == 1) :
            print "ok"
            sent = connexion.send("<rRegister msg=\"emailUsed\" />\0")
            if sent == 0 :
                raise RuntimeError
            else :
                print sent, "characters sent"
        else :
            r = "insert into users (email, pwd, firstJoin) values(\""+mail+"\", \""+pwd+"\", now())"
            print r
            b.req(r)
            sent = connexion.send("<rRegister msg=\"user inserted\" />\0")
            if sent == 0 :
                raise RuntimeError
            else :
                print sent, "characters sent"

    if tag == "message" :
        print "#####################"
        sender = handler.From
        recipient = handler.To
        content = handler.content
        print "from:", sender
        print "to:", recipient
        print "message:", content
        #mail = users[sender]['mail']
        if recipient != "0" :
            sendToUser(sender, recipient, content)
        else :
            sendToAll(sender, buff, "panu")

    if tag == "move" :
        sender = handler.charac_id

    if tag == "getMap" :
        l = LandSquares(handler.topLeft, handler.bottomRignt)
        connexion.send(l.formatXML())
            
    connexion.close()
        
###########################################################################




            
#connect to the database
b = Base.Base()
b.connect()


# creation de socket
mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mySocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

print "socket.gethostname()", socket.gethostname()

"""
here we use socket.gethostname() so that the socket would be
visible from the outside. if we had used (("localhost",PORT))
the socket would be only visible within the same machine

HOST = '' really works
"""
try:
    mySocket.bind((HOST, PORT))
except socket.error:
    print "Error socket.bind("+socket.gethostname()+", "+str(PORT)+") "
    #os.strerror()
    sys.exit()
    
"""
# a queue
q = Queue.Queue()
q.put(mySocket)
"""

mySocket.listen(10)
#connexion = private socket
while 1 :
    print "Server waiting for query"
    connexion, adresse = mySocket.accept()
    #connexion.settimeout(3)
    print "connection accepted from", adresse
    print "socket connexion", connexion
    # lancer un thread pour accueillir des clients
    t = threading.Thread(target = waitForUser, args = (connexion,))
    t.start()
    #t.join()


#fermer = raw_input("[serveur]")
#if fermer == "S_FERMER" :
#    mySocket.close()
#    print("Server shut down")

